using System;
using System.Collections.Generic;
using System.Text;
using Sixeyed.CacheAdapter.Interfaces;
using Sixeyed.CacheAdapter.Bases;
using Alachisoft.NCacheExpress.Web.Caching;
using System.Collections;

namespace Sixeyed.CacheAdapter.NCacheExpressProvider
{
    /// <summary>
    /// Cache provider wrapping access to underlying NCache Express store
    /// </summary>
    public class NCacheExpressProvider : BaseCacheProvider<NCacheExpressProviderConfiguration>
    {
        private static object _cacheSyncLock = new object();
        private static object _configSyncLock = new object();
        private static Cache _cache;
        private static NCacheExpressProviderConfiguration _config;

        /// <summary>
        /// Gets/sets the configuration of the cache instance 
        /// </summary>
        public override NCacheExpressProviderConfiguration Configuration
        {
            get { return _config; }
            set
            {
                lock (_configSyncLock)
                {
                    _config = value;
                }
            }
        }

        /// <summary>
        /// Returns whether the cache provider has been initialized
        /// </summary>
        public override bool IsInitialized
        {
            get { return (_cache != null); }
        }

        /// <summary>
        /// Initializes the cache instance using the given configuration
        /// </summary>
        /// <param name="configuration">Cache instance configuration</param>
        public override void Initialize(ICacheProviderConfiguration configuration)
        {
            //check the cache is not already initialized:
            if (!IsInitialized)
            {
                this.Configuration = configuration as NCacheExpressProviderConfiguration;
                if (this.Configuration == null)
                {
                    throw new NCacheExpressProviderException("Provider configuration not compatible. Expected NCacheExpressProviderConfiguration, received: {0}", configuration.GetType().Name);
                }
                lock (_cacheSyncLock)
                {
                    try
                    {
                        _cache = NCache.InitializeCache(this.Configuration.CacheId);
                    }
                    catch (Exception ex)
                    {
                        throw new NCacheExpressProviderException(ex, "Error initializing NCache Express store, cache id: {0}", this.Configuration.CacheId);
                    }
                }
            }
        }

        /// <summary>
        /// Inserts/updates an object in the cache with the configured default lifespan
        /// </summary>
        /// <param name="cacheKey">Cache key</param>
        /// <param name="serializableObject">Object to cache</param>
        public override void Put(string cacheKey, object serializableObject)
        {
            this.Put(cacheKey, serializableObject, _config.DefaultLifespan);
        }

        /// <summary>
        /// Inserts/updates an object in the cache with the given lifespan
        /// </summary>
        /// <param name="cacheKey">Cache key</param>
        /// <param name="serializableObject">Object to cache</param>
        /// <param name="lifespan">Lifespan of the object in the cache</param>
        public override void Put(string cacheKey, object serializableObject, TimeSpan lifespan)
        {
            this.CheckCacheInitialized();
            try
            {
                lock (_cacheSyncLock)
                {
                    _cache.Insert(cacheKey, serializableObject, null, Cache.NoAbsoluteExpiration, lifespan, CacheItemPriority.Default);
                }
            }
            catch (Exception ex)
            {
                throw new NCacheExpressProviderException(ex, "Error adding object to NCache Express store");
            }
        }

        /// <summary>
        /// Returns an object from the cache
        /// </summary>
        /// <param name="cacheKey">Cache key</param>
        /// <returns>Cached object, or null if not found</returns>
        public override object Get(string cacheKey)
        {
            this.CheckCacheInitialized();
            return _cache.Get(cacheKey);
        }

        /// <summary>
        /// Removes an object from the cache
        /// </summary>
        /// <param name="cacheKey">Cache key</param>
        public override void Remove(string cacheKey)
        {
            this.CheckCacheInitialized();
            lock (_cacheSyncLock)
            {
                _cache.Remove(cacheKey);
            }
        }

        /// <summary>
        /// Returns an enumerator over all objects in the cache
        /// </summary>
        /// <returns></returns>
        public override IEnumerator GetEnumerator()
        {
            this.CheckCacheInitialized();
            return _cache.GetEnumerator();
        }

        private void CheckCacheInitialized()
        {
            if (!IsInitialized)
            {
                throw new NCacheExpressProviderException("Attempt to access uninitialized NCache Express store");
            }
        }
    }
}
